package com.ygqh.baby.dao.impl;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;
import com.ygqh.baby.ao.Constant;
import com.ygqh.baby.ao.QueryInfo;
import com.ygqh.baby.dao.BaseMongodbDao;
import com.ygqh.baby.utils.CommonUtil;
import com.ygqh.baby.utils.ReflectionUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.*;
import java.util.regex.Pattern;

@Repository("mongoDao")
public class BaseMongodbDaoImpl<T> implements BaseMongodbDao<T> {

	public final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private MongoOperations mongoTemplate;

	public MongoOperations getMongoTemplate() {
		return mongoTemplate;
	}

	@Override
	public void saveEntity(Object entity) {
		mongoTemplate.insert(entity);
	}

	@Override
	public void saveBatch(List<? extends Object> entityList) {
		mongoTemplate.insertAll(entityList);
	}

	@Override
	public T findEntityById(Serializable id) {
		return mongoTemplate.findById(id, getEntityClass());
	}

	@Override
	public List<T> findEntityByIds(Collection<String> ids) {
		Criteria criteria = Criteria.where(getkeyName()).in(ids);
		Query query = new Query(criteria);
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public List<T> findEntityByMogoIds(Collection<String> ids) {
		Criteria criteria = Criteria.where("_id").in(ids);
		Query query = new Query(criteria);
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public void deleteEntityById(Serializable id) {
		mongoTemplate.remove(findEntityById(id));
	}

	@Override
	public void deleteEntity(Object entity) {
		mongoTemplate.remove(entity);
	}

	@Override
	public void deleteAllEntity() {
		mongoTemplate.remove(new Query(), getEntityClass());
	}

	@Override
	public void deleteEntityByConditions(Map<String, Object> conditions) {
		Query query = new Query();
		Criteria criteria = new Criteria();
		buildCriteriaByConditions(conditions, criteria);
		query.addCriteria(criteria);
		mongoTemplate.remove(query, getEntityClass());
	}

	@Override
	public void updateEntity(Object entity) {
		try {
			HashMap<String, Object> setMap = new HashMap<>();
			CommonUtil.refectPutMap(entity, setMap);
			Query query = new Query();
			Update update = new Update();
			query.addCriteria(Criteria.where(getkeyName()).is(ReflectionUtils.getFieldValue(entity, getkeyName())));
			for (Map.Entry<String, Object> mapEntry : setMap.entrySet()) {
				update.set(mapEntry.getKey(), mapEntry.getValue());
			}
			mongoTemplate.updateFirst(query, update, getEntityClass());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void saveOrUpdateEntity(Object entity) {
		try {
			HashMap<String, Object> setMap = new HashMap<>();
			CommonUtil.refectPutMap(entity, setMap);
			Query query = new Query();
			Update update = new Update();
			query.addCriteria(Criteria.where(getkeyName()).is(ReflectionUtils.getFieldValue(entity, getkeyName())));
			for (Map.Entry<String, Object> mapEntry : setMap.entrySet()) {
				update.set(mapEntry.getKey(), mapEntry.getValue());
			}
			mongoTemplate.upsert(query, update, getEntityClass());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void updateByMap(HashMap<String, Object> updateMap, Serializable id) {
		try {
			Query query = new Query();
			Update update = new Update();
			query.addCriteria(Criteria.where(getkeyName()).is(id));
			for (Map.Entry<String, Object> mapEntry : updateMap.entrySet()) {
				update.set(mapEntry.getKey(), mapEntry.getValue());
			}
			mongoTemplate.updateFirst(query, update, getEntityClass());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void updateEntity(Map<String, Object> updateMap, String id) {
		try {
			Query query = new Query();
			query.addCriteria(Criteria.where(getkeyName()).is(id));
			Update update = new Update();
			for (Map.Entry<String, Object> mapEntry : updateMap.entrySet()) {
				if (mapEntry.getKey().equals("inc")) {
					for (Map.Entry<String, Number> map : ((Map<String, Number>) mapEntry.getValue()).entrySet()) {
						update.inc(map.getKey(), map.getValue());
					}
				} else {
					update.set(mapEntry.getKey(), mapEntry.getValue());
				}

			}
			mongoTemplate.updateFirst(query, update, getEntityClass());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void updateEntities(Map<String, Object> updateMap, Map<String, Object> conditions) {
		try {
			Update update = new Update();
			for (Map.Entry<String, Object> mapEntry : updateMap.entrySet()) {
				if (mapEntry.getKey().equals("inc")) {
					for (Map.Entry<String, Number> map : ((Map<String, Number>) mapEntry.getValue()).entrySet()) {
						update.inc(map.getKey(), map.getValue());
					}
				} else if (mapEntry.getKey().equals("pull")) {
					for (Map.Entry<String, String> map : ((Map<String, String>) mapEntry.getValue()).entrySet()) {
						update.pull(map.getKey(), map.getValue());
					}
				} else if (mapEntry.getKey().equals("push")) {
					for (Map.Entry<String, Object> map : ((Map<String, Object>) mapEntry.getValue()).entrySet()) {
						if (map.getValue() instanceof Collection) {
							update.pushAll(map.getKey(), ((List<String>) map.getValue()).toArray());
						} else {
							update.addToSet(map.getKey(), map.getValue());
						}
					}
				} else if (mapEntry.getKey().equals("$")) {
					for (Map.Entry<String, String> map : ((Map<String, String>) mapEntry.getValue()).entrySet()) {
						update.set(map.getKey(), map.getValue());
					}
				} else if (mapEntry.getKey().equals("unset")) {
					update.unset(String.valueOf(mapEntry.getValue()));
				} else {
					update.set(mapEntry.getKey(), mapEntry.getValue());
				}
			}
			Criteria criteria = new Criteria();
			Query query = new Query();
			buildCriteriaByConditions(conditions, criteria);
			query.addCriteria(criteria);
			mongoTemplate.updateMulti(query, update, getEntityClass());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public long getEntityCount(Map<String, Object> conditions) {
		Criteria criteria = new Criteria();
		buildCriteriaByConditions(conditions, criteria);
		Query query = new Query();
		query.addCriteria(criteria);
		return mongoTemplate.count(query, getEntityClass());
	}

	@Override
	public long getEntityCount(Criteria criteria) {
		Query query = new Query();
		query.addCriteria(criteria);
		return mongoTemplate.count(query, getEntityClass());
	}

	@Override
	public List<T> getEntityList(Map<String, Object> conditions, Map<String, String> orders) {
		Criteria criteria = new Criteria();
		buildCriteriaByConditions(conditions, criteria);
		Query query = new Query();
		query.addCriteria(criteria);
		if (orders != null && orders.size() > 0) {
			for (Map.Entry<String, String> entry : orders.entrySet()) {
				query.with(new Sort(new Sort.Order(entry.getValue().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, entry.getKey())));
			}
		}
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public List<T> getEntityList(Criteria criteria) {
		Query query = new Query();
		query.addCriteria(criteria);
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public List<T> getEntityList(QueryInfo queryInfo, Criteria criteria, Map<String, String> orders) {
		Query query = new Query();
		query.addCriteria(criteria);
		if (queryInfo != null) {
			query.skip(queryInfo.getStart()).limit(queryInfo.getLimit());
		}
		if (orders != null && orders.size() > 0) {
			for (Map.Entry<String, String> entry : orders.entrySet()) {
				query.with(new Sort(new Sort.Order(entry.getValue().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, entry.getKey())));
			}
		}
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public List<T> getPageEntityList(QueryInfo queryInfo, Map<String, Object> conditions, Map<String, String> orders) {
		Criteria criteria = new Criteria();
		buildCriteriaByConditions(conditions, criteria);
		Query query = new Query();
		query.addCriteria(criteria);
		if (queryInfo != null && queryInfo.getLimit() > 0) {
			query.skip(queryInfo.getStart()).limit(queryInfo.getLimit());
		}
		if (orders != null && orders.size() > 0) {
			for (Map.Entry<String, String> entry : orders.entrySet()) {
				query.with(new Sort(new Sort.Order(entry.getValue().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, entry.getKey())));
			}
		}
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public List<T> getPageEntityList(QueryInfo queryInfo, Criteria criteria, Map<String, String> orders) {
		Query query = new Query();
		query.addCriteria(criteria);
		if (queryInfo != null) {
			query.skip(queryInfo.getStart()).limit(queryInfo.getLimit());
		}
		if (orders != null && orders.size() > 0) {
			for (Map.Entry<String, String> entry : orders.entrySet()) {
				query.with(new Sort(new Sort.Order(entry.getValue().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, entry.getKey())));
			}
		}
		return mongoTemplate.find(query, getEntityClass());
	}

	@SuppressWarnings("unchecked")
	private void buildCriteriaByConditions(Map<String, Object> conditions, Criteria criteria) {
		if (criteria == null) {
			criteria = new Criteria();
		}
		if (conditions != null && conditions.size() > 0) {
			for (Map.Entry<String, Object> entry : conditions.entrySet()) {
				if ("exists".equals(entry.getKey())) {
					for (Map.Entry<String, Object> size : ((Map<String, Object>) entry.getValue()).entrySet()) {
						criteria.and(size.getKey()).exists((Boolean) size.getValue());
					}
				} else if ("ne".equals(entry.getKey())) {
					for (Map.Entry<String, Object> map : ((Map<String, Object>) entry.getValue()).entrySet()) {
						if (map.getValue() instanceof Collection) {
							criteria.and(map.getKey()).nin((List<?>) map.getValue());
						} else {
							criteria.and(map.getKey()).ne(map.getValue());
						}
					}
				} else if ("elemMatch".equals(entry.getKey())) {
					Criteria match = new Criteria();
					String field = null;
					for (Map.Entry<String, Object> map : ((Map<String, Object>) entry.getValue()).entrySet()) {
						if ("key".equals(map.getKey())) {
							field = map.getValue().toString();
						} else {
							match.and(map.getKey()).is(map.getValue());
						}
					}
					criteria.and(field).elemMatch(match);
				} else if ("ors".equals(entry.getKey())) {
					List<Criteria> orCriterias = new ArrayList<>();
					for (Map.Entry<String, Object> map : ((Map<String, Object>) entry.getValue()).entrySet()) {
						if (map.getValue() instanceof Map) {
							Criteria chain = Criteria.where(map.getKey());
							for (Map.Entry<String, Object> submap : ((Map<String, Object>) map.getValue()).entrySet()) {
								if (submap.getKey().equals("elemMatch")) {
									chain.elemMatch(Criteria.where("$regex").is(submap.getValue()));
								} else {
									chain.is(submap.getValue());
								}
							}
							orCriterias.add(chain);
						} else if (map.getValue() instanceof Collection) {
							orCriterias.add(Criteria.where(map.getKey()).in((List<?>) map.getValue()));
						} else if ((map.getValue() instanceof String)
								&& (map.getValue().toString().startsWith("^.*") || map.getValue().toString().endsWith(".*$"))) {
							String regex = map.getValue().toString();
							regex = regex.substring(3, regex.length() - 3);
							regex = CommonUtil.escapeExprSpecialWord(regex);
							Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
							;
							orCriterias.add(Criteria.where(map.getKey()).regex(pattern));
						} else {
							orCriterias.add(Criteria.where(map.getKey()).is(map.getValue()));
						}
					}
					criteria.orOperator(orCriterias.toArray(new Criteria[0]));
				} else if ("compare".equals(entry.getKey())) {
					for (Map.Entry<String, Map<String, Object>> map : ((Map<String, Map<String, Object>>) entry.getValue()).entrySet()) {
						Criteria _criteria = Criteria.where(map.getKey());
						for (Map.Entry<String, Object> _map : ((Map<String, Object>) map.getValue()).entrySet()) {
							if (_map.getKey().equals("gt")) {
								_criteria.gt(_map.getValue());
							}
							if (_map.getKey().equals("gte")) {
								_criteria.gte(_map.getValue());
							}
							if (_map.getKey().equals("lt")) {
								_criteria.lt(_map.getValue());
							}
							if (_map.getKey().equals("lte")) {
								_criteria.lte(_map.getValue());
							}
							if (_map.getKey().equals("nin")) {
								_criteria.nin((List<?>) _map.getValue());
							}
						}
						criteria.andOperator(_criteria);
					}
				} else if (entry.getValue() instanceof Collection) {
					criteria.and(entry.getKey()).in((List<?>) entry.getValue());
				} else if ((entry.getValue() instanceof String)
						&& (entry.getValue().toString().startsWith("^.*") || entry.getValue().toString().endsWith(".*$"))) {
					String regex = entry.getValue().toString();
					regex = regex.substring(3, regex.length() - 3);
					regex = CommonUtil.escapeExprSpecialWord(regex);
					Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
					;
					criteria.and(entry.getKey()).regex(pattern);
				} else {
					criteria.and(entry.getKey()).is(entry.getValue());
				}
			}
		}
	}

	public <O> List<O> getGroupEntity(Aggregation agg, Class<O> outClass) {
		return mongoTemplate.aggregate(agg, getEntityClass(), outClass).getMappedResults();
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, String _collName, int _skip, int _limit, String _field, int _sort) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notNull(_criteria, "_collName" + Constant.IS_NULL);
		Query query = new Query();
		query.addCriteria(_criteria).skip(_skip).limit(_limit).with(new Sort(new Sort.Order(_sort >= 0 ? Sort.Direction.ASC : Sort.Direction.DESC, _field)));
		List<T> list = (List<T>) mongoTemplate.find(query, getEntityClass(), _collName);
		return list;
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, int _skip, int _limit, String _field, int _sort) throws Exception {

		return this.findDatasForPage(_criteria, this.getCollectionName(), _skip, _limit, _field, _sort);
	}

	@Override
	public void insert(Object _data, String _collName) throws Exception {

		Validate.notNull(_data, "_data" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		mongoTemplate.insert(_data, _collName);
	}

	@Override
	public void insert(Object _data) {
		mongoTemplate.insert(_data);
	}

	@Override
	public int delete(Criteria _criteria, String _collName) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria);

		WriteResult result = mongoTemplate.remove(query, getEntityClass(), _collName);
		if (result != null) {
			return result.getN();
		}
		return 0;
	}

	@Override
	public int delete(Criteria _criteria) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Query query = new Query();
		query.addCriteria(_criteria);
		WriteResult result = mongoTemplate.remove(query, getEntityClass());
		if (result != null) {
			return result.getN();
		}

		return 0;
	}

	@Override
	public int delete(Criteria _criteria, String _collName, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria);

		WriteResult result = mongoTemplate.remove(query.limit(_limit), getEntityClass(), _collName);
		if (result != null) {
			return result.getN();
		}

		return 0;
	}

	@Override
	public int delete(Criteria _criteria, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria);

		WriteResult result = mongoTemplate.remove(query.limit(_limit), getEntityClass());
		if (result != null) {
			return result.getN();
		}

		return 0;
	}

	@Override
	public int delete(List<Criteria> _criterias, String _collName) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		int deleteCount = 0;
		for (Criteria criteria : _criterias) {
			int resultInt = this.delete(criteria, _collName);
			if (resultInt > 0) {
				deleteCount++;
			}
		}
		return deleteCount;
	}

	@Override
	public int delete(List<Criteria> _criterias) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);

		int deleteCount = 0;
		for (Criteria criteria : _criterias) {
			int resultInt = this.delete(criteria);
			if (resultInt > 0) {
				deleteCount++;
			}
		}
		return deleteCount;
	}

	@Override
	public int deleteById(String id) throws Exception {
		Validate.notNull(id, "id" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(Criteria.where("_id").is(id));

		WriteResult result = mongoTemplate.remove(query, getEntityClass());
		if (result != null) {
			return result.getN();
		}
		return 0;
	}

	@Override
	public int deleteByIds(String... _ids) throws Exception {
		Validate.notNull(_ids, "_ids" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(Criteria.where("_id").in(Arrays.asList(_ids)));

		WriteResult result = mongoTemplate.remove(query, getEntityClass());
		if (result != null) {
			return result.getN();
		}
		return 0;
	}

	@Override
	public int deleteById(String _collName, String id) throws Exception {
		Validate.notNull(id, "id" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(Criteria.where("_id").is(id));

		WriteResult result = mongoTemplate.remove(query, getEntityClass(), _collName);
		if (result != null) {
			return result.getN();
		}
		return 0;
	}

	@Override
	public int deleteByIds(String _collName, String... _ids) throws Exception {
		Validate.notNull(_ids, "_ids" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(Criteria.where("_id").in(Arrays.asList(_ids)));

		WriteResult result = mongoTemplate.remove(query, getEntityClass(), _collName);
		if (result != null) {
			return result.getN();
		}
		return 0;
	}

	@Override
	public void deleteByCollName(String _collName) throws Exception {
		Validate.notEmpty(_collName, "_collName" + Constant.IS_NULL);
		mongoTemplate.remove(new Query(), _collName);
	}

	@Override
	public int deleteByCollNameAndCriteria(Criteria _criteria, String _collName, int _limit) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria);

		WriteResult result = mongoTemplate.remove(query.limit(_limit), _collName);
		if (result != null) {
			return result.getN();
		}

		return 0;
	}

	@Override
	public void update(Criteria _criteria, HashMap<String, Object> _setMap, String _collName) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notNull(_setMap, "setMap" + Constant.IS_NULL);

		Query query = new Query();
		Update update = new Update();

		query.addCriteria(_criteria);
		for (Map.Entry<String, Object> mapEntry : _setMap.entrySet()) {
			update.set(mapEntry.getKey(), mapEntry.getValue());
		}
		mongoTemplate.findAndModify(query, update, getEntityClass(), _collName);
	}

	@Override
	public void update(Criteria _criteria, HashMap<String, Object> _setMap) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notNull(_setMap, "setMap" + Constant.IS_NULL);

		Query query = new Query();
		Update update = new Update();

		query.addCriteria(_criteria);
		for (Map.Entry<String, Object> mapEntry : _setMap.entrySet()) {
			update.set(mapEntry.getKey(), mapEntry.getValue());
		}
		mongoTemplate.findAndModify(query, update, getEntityClass());
	}

	@Override
	public void update(String _columnName, Object _obj, HashMap<String, Object> _setMap, String _collName) throws Exception {
		Validate.notNull(_obj, "_obj" + Constant.IS_NULL);
		Validate.notNull(_setMap, "setMap" + Constant.IS_NULL);

		Query query = new Query();
		Update update = new Update();

		query.addCriteria(Criteria.where(_columnName).is(_obj));
		for (Map.Entry<String, Object> mapEntry : _setMap.entrySet()) {
			update.set(mapEntry.getKey(), mapEntry.getValue());
		}
		mongoTemplate.findAndModify(query, update, getEntityClass(), _collName);
	}

	@Override
	public void update(String _columnName, Object _obj, HashMap<String, Object> _setMap) throws Exception {
		Validate.notNull(_obj, "_obj" + Constant.IS_NULL);
		Validate.notNull(_setMap, "setMap" + Constant.IS_NULL);

		Query query = new Query();
		Update update = new Update();

		query.addCriteria(Criteria.where(_columnName).is(_obj));
		for (Map.Entry<String, Object> mapEntry : _setMap.entrySet()) {
			update.set(mapEntry.getKey(), mapEntry.getValue());
		}
		mongoTemplate.findAndModify(query, update, getEntityClass());
	}

	@Override
	public void update(Map<String, Object> _criteriaMap, HashMap<String, Object> _setMap, String _collName) throws Exception {
		Validate.notNull(_criteriaMap, "_criteriaMap" + Constant.IS_NULL);
		Validate.notNull(_setMap, "_setMap" + Constant.IS_NULL);

		Query query = new Query();
		Update update = new Update();

		for (Map.Entry<String, Object> criteria : _criteriaMap.entrySet()) {
			query.addCriteria(Criteria.where(criteria.getKey()).is(criteria.getValue()));
		}

		for (Map.Entry<String, Object> mapEntry : _setMap.entrySet()) {
			update.set(mapEntry.getKey(), mapEntry.getValue());
		}
		mongoTemplate.findAndModify(query, update, getEntityClass(), _collName);
	}

	@Override
	public void update(Map<String, Object> _criteriaMap, HashMap<String, Object> _setMap) throws Exception {
		Validate.notNull(_criteriaMap, "_criteriaMap" + Constant.IS_NULL);
		Validate.notNull(_setMap, "_setMap" + Constant.IS_NULL);

		Query query = new Query();
		Update update = new Update();

		for (Map.Entry<String, Object> criteria : _criteriaMap.entrySet()) {
			query.addCriteria(Criteria.where(criteria.getKey()).is(criteria.getValue()));
		}

		for (Map.Entry<String, Object> mapEntry : _setMap.entrySet()) {
			update.set(mapEntry.getKey(), mapEntry.getValue());
		}
		mongoTemplate.findAndModify(query, update, getEntityClass());
	}

	@Override
	public T findData(Criteria _criteria, String _collName) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria);
		T t = mongoTemplate.findOne(query, getEntityClass(), _collName);

		return t;
	}

	@Override
	public T findData(Criteria _criteria) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria);
		T t = mongoTemplate.findOne(query, getEntityClass());

		return t;
	}

	@Override
	public List<T> findDatas(Criteria _criteria, String _collName) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria);
		List<T> list = mongoTemplate.find(query, getEntityClass(), _collName);

		return list;
	}

	@Override
	public List<T> findDatas(Criteria _criteria) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria);
		List<T> list = mongoTemplate.find(query, getEntityClass());

		return list;
	}

	@Override
	public List<T> findDatas(List<Criteria> _criterias, String _collName) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		List<T> list = new ArrayList<>();
		for (Criteria criteria : _criterias) {
			T t = this.findData(criteria, _collName);
			if (t != null) {
				list.add(t);
			}
		}
		return list;
	}

	@Override
	public List<T> findDatas(List<Criteria> _criterias) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);

		List<T> list = new ArrayList<>();
		for (Criteria criteria : _criterias) {
			T t = this.findData(criteria);
			if (t != null) {
				list.add(t);
			}
		}
		return list;
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, String _collName, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria).limit(_limit);
		List<T> list = mongoTemplate.find(query, getEntityClass(), _collName);

		return list;
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria).limit(_limit);
		List<T> list = mongoTemplate.find(query, getEntityClass());

		return list;
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, String _collName, int _skip, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria).skip(_skip).limit(_limit).with(new Sort(new Sort.Order(Sort.Direction.ASC, "createTime")));

		List<T> list = mongoTemplate.find(query, getEntityClass(), _collName);

		return list;
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, int _skip, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria).skip(_skip).limit(_limit).with(new Sort(new Sort.Order(Sort.Direction.ASC, "createTime")));

		List<T> list = mongoTemplate.find(query, getEntityClass());

		return list;
	}

	@Override
	public List<T> findDatasForPage(List<Criteria> _criterias, String _collName, int _limit) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		List<T> list = new ArrayList<>();
		for (Criteria criteria : _criterias) {
			List<T> t = this.findDatasForPage(criteria, _collName, _limit);
			if (t != null) {
				list.addAll(t);
			}
		}
		return list;
	}

	@Override
	public List<T> findDatasForPage(List<Criteria> _criterias, int _limit) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		List<T> list = new ArrayList<>();
		for (Criteria criteria : _criterias) {
			List<T> t = this.findDatasForPage(criteria, _limit);
			if (t != null) {
				list.addAll(t);
			}
		}
		return list;
	}

	@Override
	public List<T> findDatasForPage(List<Criteria> _criterias, String _collName, int _skip, int _limit) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		List<T> list = new ArrayList<>();
		for (Criteria criteria : _criterias) {
			List<T> t = this.findDatasForPage(criteria, _collName, _skip, _limit);
			if (t != null) {
				list.addAll(t);
			}
		}
		return list;
	}

	@Override
	public List<T> findDatasForPage(List<Criteria> _criterias, int _skip, int _limit) throws Exception {

		Validate.notNull(_criterias, "_criterias" + Constant.IS_NULL);
		Validate.notNull(_limit, "_limit" + Constant.IS_NULL);

		List<T> list = new ArrayList<>();
		for (Criteria criteria : _criterias) {
			List<T> t = this.findDatasForPage(criteria, _skip, _limit);
			if (t != null) {
				list.addAll(t);
			}
		}
		return list;
	}

	@Override
	public List<T> findDatasForPage(Criteria _criteria, int _skip, int _limit, LinkedHashMap<String, String> _sortMap) {
		Query query = new Query();
		if (_criteria != null) {
			query.addCriteria(_criteria);
		}
		query.skip(_skip).limit(_limit);
		if (_sortMap != null && _sortMap.size() > 0) {
			for (Map.Entry<String, String> entry : _sortMap.entrySet()) {
				query.with(new Sort(new Sort.Order(entry.getValue().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, entry.getKey())));
			}
		} else {
			query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "_id")));
		}
		return mongoTemplate.find(query, getEntityClass());
	}

	public List<T> findAndDeleteDatasForPage(Criteria _criteria, String _collName, int _skip, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria).skip(_skip).limit(_limit);
		return mongoTemplate.findAllAndRemove(query, _collName);
	}

	public List<T> findAndDeleteDatasForPage(Criteria _criteria, int _skip, int _limit) throws Exception {

		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);

		Query query = new Query();
		query.addCriteria(_criteria).skip(_skip).limit(_limit);
		return mongoTemplate.findAllAndRemove(query, getEntityClass());
	}

	@Override
	public long count(String _collName) throws Exception {

		Validate.notEmpty(_collName, "_collName" + Constant.IS_NULL);

		return mongoTemplate.count(null, _collName);
	}

	@Override
	public long count() {
		return mongoTemplate.count(null, getEntityClass());
	}

	@Override
	public long count(Criteria _criteria, String _collName) throws Exception {
		Validate.notNull(_criteria, "_criteria" + Constant.IS_NULL);
		Validate.notEmpty(_collName, "_collName" + Constant.IS_EMPTY);

		Query query = new Query();
		query.addCriteria(_criteria);

		return mongoTemplate.count(query, _collName);
	}

	@Override
	public long count(Criteria _criteria) {
		Query query = null;
		if (_criteria != null) {
			query = new Query();
			query.addCriteria(_criteria);
		}
		return mongoTemplate.count(query, getEntityClass());
	}

	@Override
	public boolean collectionExists(String _collName) throws Exception {
		Validate.notEmpty(_collName, "_collName" + Constant.IS_NULL);

		return mongoTemplate.collectionExists(_collName);
	}

	@Override
	public boolean collectionExists() throws Exception {
		return mongoTemplate.collectionExists(getEntityClass());
	}

	@SuppressWarnings("unchecked")
	@Override
	public Map<String, Object> findDatasGroup(String reduce, Criteria _criteria, String _collName, String _fieldName) {

		Query query = Query.query(_criteria);
		DBObject result = mongoTemplate.getCollection(_collName).group(new BasicDBObject(_fieldName, 1), query.getQueryObject(), new BasicDBObject("count", 0),
				reduce);
		Map<String, Object> map = result.toMap();
		return map;
	}

	@Override
	public Map<String, Object> findDatasGroup(String reduce, Criteria _criteria, String _fieldName) {

		return this.findDatasGroup(reduce, _criteria, this.getCollectionName(), _fieldName);
	}

	@Override
	public List<T> getPropertyGroupData(Aggregation agg, String _collName, Class<T> outClass) {
		AggregationResults<T> groupResults = mongoTemplate.aggregate(agg, _collName, outClass);
		List<T> result = groupResults.getMappedResults();

		return result;
	}

	@Override
	public List<T> getPropertyGroupData(Aggregation agg, Class<T> outClass) {
		return this.getPropertyGroupData(agg, this.mongoTemplate.getCollectionName(outClass), outClass);
	}

	public Class<T> getEntityClass() {
		return ReflectionUtils.getSuperClassGenricType(getClass());
	}

	private String getkeyName() {
		return ReflectionUtils.getKeyName(ReflectionUtils.getSuperClassGenricType(getClass()));
	}

	private String getCollectionName() {
		return this.getMongoTemplate().getCollectionName(this.getEntityClass());
	}

}
